package com.only4play.portal.common.api;

import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * <pre>
 * 默认消息重复检查器
 * 将每个消息id保存在内存里，每隔5秒清理已经过期的消息id，每个消息id的过期时间是15秒
 * </pre>
 *
 * @author liyuncong
 * @version 1.0
 * @file PortalMessageInMemoryDuplicateChecker
 * @brief PortalMessageInMemoryDuplicateChecker
 * @details PortalMessageInMemoryDuplicateChecker
 * @date 2024-06-07
 *
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                     NAME               DESCRIPTION
 * 2024-06-07               liyuncong          Created
 */
public class MessageInMemoryDuplicateChecker implements MessageDuplicateChecker {

    /**
     * 一个消息ID在内存的过期时间：15秒
     */
    private final Long timeout;

    /**
     * 每隔多少周期检查消息ID是否过期：5秒
     */
    private final Long clearPeriod;

    /**
     * 消息id->消息时间戳 map
     */
    private final ConcurrentHashMap<String, Long> msgId2Timestamp = new ConcurrentHashMap<>();

    /**
     * 后台清理线程是否已经开启，默认关闭
     */
    private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false);

    public MessageInMemoryDuplicateChecker() {
        this.timeout = 15 * 1000L;
        this.clearPeriod = 5 * 1000L;
    }

    public MessageInMemoryDuplicateChecker(Long timeout, Long clearPeriod) {
        this.timeout = timeout;
        this.clearPeriod = clearPeriod;
    }

    @Override
    public boolean isDuplicate(String messageId) {
        if (messageId == null) {
            return false;
        }
        checkBackgroundProcessStarted();
        Long timestamp = this.msgId2Timestamp.putIfAbsent(messageId, Instant.now().toEpochMilli());
        return timestamp != null;
    }

    protected void checkBackgroundProcessStarted() {
        if (this.backgroundProcessStarted.getAndSet(true)) {
            return;
        }
        Thread thread = new Thread(() -> {
            try {
                while (true) {
                    Thread.sleep(MessageInMemoryDuplicateChecker.this.clearPeriod);
                    long now = Instant.now().toEpochMilli();
                    MessageInMemoryDuplicateChecker.this.msgId2Timestamp.entrySet()
                        .removeIf(entry -> now - entry.getValue() > MessageInMemoryDuplicateChecker.this.timeout);
                }
            } catch (InterruptedException exception) {
                Thread.currentThread().interrupt();
            }
        });
        thread.setDaemon(true);
        thread.start();
    }
}
